/**
 * ExpressionEval ExpressionEval.java
 */

package CCT220_Trabalho06_13106;


import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JOptionPane;


/**
 * <div style="border-style:double;">
 * Project: CCT220
 * </br>Package: CCT220_Trabalho06_13106
 * </br>Class: ExpressionEval
 * </br>Created by marcel
 * </br>Description: Classe que avalia uma expressão e retorna o valor
 * </div>
 * 
 * @author marcel
 * @author 13106
 * 
 */
public class ExpressionEval {

	/**
	 * String exp
	 */
	private String exp;

	/**
	 * String result
	 */
	private String result;


	/**
	 * <div style="border-style:double;">
	 * Constructor for
	 * </br>CCT220_Trabalho06_13106::ExpressionEval
	 * </br>LastChange: 05/11/2011 10:04:18 by marcel
	 * </div>
	 * 
	 * @param exp
	 */
	public ExpressionEval(String exp) {

		this.exp = exp;
		this.result = avaliaExp(exp);
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::getExp
	 * </br>Last Change: 05/11/2011 10:04:06 by marcel
	 * </br>Description: Getter for exp
	 * </div>
	 * 
	 * @return
	 */
	public String getExp() {

		return exp;
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::getResult
	 * </br>Last Change: 05/11/2011 10:03:28 by marcel
	 * </br>Description: Getter for result
	 * </div>
	 * 
	 * @return the result
	 */
	public String getResult() {

		return result;
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::setExp
	 * </br>Last Change: 05/11/2011 10:02:58 by marcel
	 * </br>Description: Setter for exp. Avalia a nova expressao passda.
	 * </div>
	 * 
	 * @param exp
	 *            the exp to set
	 */
	public void setExp(String exp) {

		this.exp = exp;
		this.result = avaliaExp(exp);
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::avaliaExp
	 * </br>Last Change: 05/11/2011 10:02:29 by marcel
	 * </br>Description: Verifica se a expressao e bem formada e caso sim
	 * avalia-a
	 * </div>
	 * 
	 * @param exp
	 * @return a String
	 */
	public String avaliaExp(String exp) {

		String log = "Verificando parenteses...";
		String result = " ";
		if (testaParenteses(exp)) {
			log += "\n\tCorreto.";
			log += "\nVerificando operadores...";
			if (testaOperadores(exp)) {
				log += "\tCorreto.";
				result = AvaliarExpressao(exp);
			} else {
				log += "\n\tExpressao mal formada.";
				JOptionPane.showMessageDialog(null, log, "Erro",
				        JOptionPane.ERROR_MESSAGE);
				result = "0";
			}
		} else {
			log += "\n\tExpressao mal formada.";
			JOptionPane.showMessageDialog(null, log, "Erro",
			        JOptionPane.ERROR_MESSAGE);
			result = "0";
		}

		return result;

	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::testaOperadores
	 * </br>Last Change: 05/11/2011 10:02:12 by marcel
	 * </br>Description: Testa se os operadores estao bem colocados
	 * </div>
	 * 
	 * @param exp
	 * @return true | false
	 */
	private boolean testaOperadores(String exp) {

		boolean flag = true;

		exp = exp.replaceAll("\\(", "");
		exp = exp.replaceAll("\\)", "");

		if (isOperador(exp.charAt(0))
		        || isOperador(exp.charAt(exp.length() - 1))) { return false; }
		for (int i = 1; i < exp.length() - 1; i++) {
			if (isOperador(exp.charAt(i))) {
				if (isOperando(exp.charAt(i - 1))
				        && isOperando(exp.charAt(i + 1))) {
					flag &= true;
				} else {
					flag = false;
				}
			}
		}
		return flag;
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::testaParenteses
	 * </br>Last Change: 05/11/2011 10:01:53 by marcel
	 * </br>Description: Testa se os parenteses estao bem colocados
	 * </div>
	 * 
	 * @param exp
	 * @return true | false
	 */
	private boolean testaParenteses(String exp) {

		String k = "";
		int count = 0;

		for (int i = 0; i < exp.length(); i++) {
			switch (exp.charAt(i)) {
				case '(':
					k += "d";
					count += 2;
					break;
				case ')':
					k += "b";
					count--;
					break;
			}
		}

		do {
			for (int i = 0; i < count; i++) {
				k = k.replaceAll("db", "");
			}
			if (k.isEmpty()) { return true; }
		} while (k.isEmpty());

		return false;
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::obterPrioridade
	 * </br>Last Change: 05/11/2011 10:01:35 by marcel
	 * </br>Description: Retorna uma prioridade para cada operador
	 * </div>
	 * 
	 * @param s
	 * @return an int
	 */
	private int obterPrioridade(String s) {

		int retorno = 0;
		char c = s.charAt(0);
		switch (c) {
			case '(':
				retorno = 1;
				break;
			case '+':
				retorno = 2;
				break;
			case '-':
				retorno = 2;
				break;
			case '*':
				retorno = 3;
				break;
			case '\\':
				retorno = 3;
				break;
			case '^':
				retorno = 4;
				break;
		}
		return retorno;

	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::isOperando
	 * </br>Last Change: 05/11/2011 10:01:03 by marcel
	 * </br>Description: Verifica se o parametro de entrada e um operando
	 * </div>
	 * 
	 * @param c
	 * @return true | false
	 */
	private boolean isOperando(char c) {

		String n = ".0123456789";
		boolean retorno = false;

		if (n.contains(String.valueOf(c))) {
			retorno = true;
		}

		return retorno;

	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::isOperador
	 * </br>Last Change: 05/11/2011 09:41:29 by marcel
	 * </br>Description: Verifica se o parametro de entrada e um operador *
	 * </div>
	 * 
	 * @param c
	 * @return
	 */
	private boolean isOperador(char c) {

		String n = "+-*\\/^";
		boolean retorno = false;

		if (n.contains(String.valueOf(c))) {
			retorno = true;
		}

		return retorno;

	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::isOperando
	 * </br>Last Change: 05/11/2011 10:04:57 by marcel
	 * </br>Description: Verifica se o parametro de entrada e um operando
	 * </div>
	 * 
	 * @param s
	 * @return true | false
	 */
	private boolean isOperando(String s) {

		try {
			Double.parseDouble(s);
			return true;
		} catch (Exception e) {
			return false;
		}
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::AvaliarExpressao
	 * </br>Last Change: 05/11/2011 10:05:28 by marcel
	 * </br>Description: Avalia a expressao dada
	 * </div>
	 * 
	 * @param expInfixa
	 * @return a String
	 */
	private String AvaliarExpressao(String expInfixa) {

		LinkedList<String> pilha = new LinkedList<String>();
		List<String> infixExpressionList = stringToList(expInfixa);
		List<String> posfixExpressionList = infixaPosfixa(infixExpressionList);

		Iterator<String> it = posfixExpressionList.iterator();
		while (it.hasNext()) {
			String s = it.next();
			if (isOperando(s)) {
				pilha.push(s);
			} else {
				if (isOperador(s.charAt(0))) {
					double y = Double.parseDouble(pilha.pop().toString());
					double x = Double.parseDouble(pilha.pop().toString());
					if (s.equals("+")) {
						pilha.push(String.valueOf(x + y));
					} else if (s.equals("-")) {
						pilha.push(String.valueOf(x - y));
					} else if (s.equals("*")) {
						pilha.push(String.valueOf(x * y));
					} else if (s.equals("/")) {
						pilha.push(String.valueOf(x / y));
					} else if (s.equals("^")) {
						pilha.push(String.valueOf(Math.pow(x, y)));
					}
				}
			}
		}
		String resultado = "";
		try {
			resultado = pilha.pop();
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, "Erro: \nDivisao por zero",
			        "Erro", JOptionPane.ERROR_MESSAGE);
			return " ";
		}
		return resultado;
	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::infixaPosfixa
	 * </br>Last Change: 05/11/2011 10:06:03 by marcel
	 * </br>Description: Converte a expressao infixa em posfixa
	 * </div>
	 * 
	 * @param infixExpressionList
	 * @return a list
	 */
	private List<String> infixaPosfixa(List<String> infixExpressionList) {

		List<String> resultado = new LinkedList<String>();
		LinkedList<String> pilha = new LinkedList<String>();
		int prioridade;

		Iterator<String> it = infixExpressionList.iterator();
		while (it.hasNext()) {
			String s = it.next();
			if (isOperando(s)) {
				resultado.add(s);
			} else if (isOperador(s.charAt(0))) {
				prioridade = obterPrioridade(String.valueOf(s));
				while ((!pilha.isEmpty())
				        && (obterPrioridade(pilha.peek()) >= prioridade)) {
					resultado.add(pilha.pop());
				}
				pilha.push(s);
			} else if (s.equals("(")) {
				pilha.push(s);
			} else if (s.equals(")")) {
				String item = "";
				do {
					item = pilha.pop();
					resultado.add(item);
				} while (!item.equals("(") && !pilha.isEmpty());
			}

		}
		while (!pilha.isEmpty()) {
			resultado.add(pilha.pop());
		}

		while (resultado.contains("(")) {
			resultado.remove("(");
		}

		return resultado;

	}


	/**
	 * <div style="border-style:double;">
	 * Metho:CCT220_Trabalho06_13106::ExpressionEval::stringToList
	 * </br>Last Change: 05/11/2011 10:06:42 by marcel
	 * </br>Description: Converte uma expressao em forma de string para uma
	 * expressao em forma de lista
	 * </div>
	 * 
	 * @param expInfixa
	 * @return a list
	 */
	private List<String> stringToList(String expInfixa) {

		List<String> list = new LinkedList<String>();
		String num = "";
		for (int i = 0; i < expInfixa.length(); i++) {
			char c = expInfixa.charAt(i);
			if (isOperando(c)) {
				num += c;
			} else {
				if (!num.equals("")) {
					list.add(num);
				}
				list.add(String.valueOf(c));
				num = "";
			}
		}
		if (!num.equals("")) {
			list.add(num);
		}
		return list;
	}

}
